package com.company.beauty.common.handler;

import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.company.beauty.common.config.properties.LarkNotificationProperties;
import com.company.beauty.common.utils.HttpMessageResultUtil;
import com.company.beauty.common.utils.LarkNotificationUtil;
import com.company.nuwa.common.domain.HttpMessageResult;
import com.company.nuwa.common.enumration.GlobalBizCode;
import com.company.nuwa.common.exception.BizException;
import com.company.nuwa.common.utils.RequestContextUtil;
import io.micrometer.common.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.MDC;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.client.HttpClientErrorException;

/**
 * <p>全局异常处理</p >
 *
 * @author youyoukele
 * @version 1.0
 * @date 2022/9/18
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    private final LarkNotificationProperties properties = SpringUtil.getBean(LarkNotificationProperties.class);

    /**
     * 处理业务异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(BizException.class)
    public HttpMessageResult<String> handleBizException(BizException e) {
        log.info("handle uri:{}, BizException:{}", getURI(), ExceptionUtils.getStackTrace(e));
        return HttpMessageResultUtil.failure(e.getCode(), e.getMessage());
    }

    /**
     * 处理参数缺失异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public HttpMessageResult<String> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        log.warn("handle uri:{}, MethodArgumentNotValidException:{}", getURI(), ExceptionUtils.getStackTrace(e));
        BindingResult bindingResult = e.getBindingResult();
        String errorMsg = bindingResult.getFieldErrors().stream()
                .map(FieldError::getDefaultMessage)
                .toList()
                .toString();
        return HttpMessageResultUtil.failure(GlobalBizCode.ILLEGAL_ARGUMENT.getCode(), errorMsg);
    }

    /**
     * 处理参数类型不匹配异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public HttpMessageResult<String> handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
        log.warn("handle uri:{}, HttpMessageNotReadableException:{}", getURI(), ExceptionUtils.getStackTrace(e));
        return HttpMessageResultUtil.failure(GlobalBizCode.ILLEGAL_ARGUMENT.getCode(), GlobalBizCode.ILLEGAL_ARGUMENT.getMessage());
    }

    /**
     * 处理http client error
     *
     * @param e
     * @return
     */
    @ExceptionHandler(HttpClientErrorException.class)
    public HttpMessageResult<String> handleHttpClientErrorException(HttpClientErrorException e) {
        log.warn("handle uri:{}, HttpClientErrorException:{}", getURI(), ExceptionUtils.getStackTrace(e));
        return HttpMessageResultUtil.failure(GlobalBizCode.SERVICE_BUSY.getCode(), e.getMessage());
    }


    /**
     * 处理各种其他异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(Exception.class)
    public HttpMessageResult<String> handleException(Exception e) {
        log.error("handle uri:{}, Exception:{}", getURI(), ExceptionUtils.getStackTrace(e));
        // 异步飞书告警
        this.asyncSendLarkNotification(e);
        return HttpMessageResultUtil.failure(GlobalBizCode.SERVICE_BUSY.getCode(), GlobalBizCode.SERVICE_BUSY.getMessage());
    }

    /**
     * 异步处理飞书告警通知
     *
     * @param e
     */
    private void asyncSendLarkNotification(Exception e) {
        if (StringUtils.isBlank(properties.getUrl())) {
            return;
        }
        String uri = getURI();
        String traceId = getTraceId();
        ThreadUtil.execAsync(() -> {
            try {
                String content = String.format("环境:%s \n服务:%s \nTRACE-ID:%s \nuri:%s \n内容:%s \n明细:%s",
                        SpringUtil.getActiveProfile(), SpringUtil.getApplicationName(), traceId, uri, e.getMessage(), ExceptionUtils.getStackTrace(e));
                // Send notification to Lark
                String s = LarkNotificationUtil.sendLarkNotification(properties.getUrl(), properties.getTarget(), properties.getTitle(), content);
                log.info("sendLarkNotification {}", s);
            } catch (Exception ex) {
                log.warn("飞书告警失败。", ex);
            }
        });
    }

    private String getURI() {
        try {
            return RequestContextUtil.getRequestURI();
        } catch (Exception ignore) {
            return null;
        }
    }

    private String getTraceId() {
        try {
            return MDC.get("traceId");
        } catch (Exception ignore) {
            return null;
        }
    }


}
